#if defined(_MSC_VER)
#pragma once
#endif

#ifndef LM_CORE_BSDF_H
#define LM_CORE_BSDF_H

#include "lm.h"
#include "bxdf.h"
#include "geometry.h"
#include "memory.h"
#include "intersection.h"

#define MAX_BXDFS 8

class BSDF {
public:
	BSDF(const Intersection &isect, float e = 1.f) {
		nBxDFs = 0;
		eta = e;
		ng = isect.geometryFrame.n;
		shadingFrame = isect.shadingFrame;
	}
	inline Vector3 WorldToLocal(const Vector3 &v) const {
		return shadingFrame.ToLocal(v);
	}
	inline Vector3 LocalToWorld(const Vector3 &v) const {
		return shadingFrame.ToWorld(v);
	}
	inline void Add(BxDF *b) {
		Assert(nBxDFs < MAX_BXDFS);
		bxdfs[nBxDFs++] = b;
	}
	inline bool HasShadingGeometry() const {
		return ng != shadingFrame.n;
	}
	inline int NumComponent() const {
		return nBxDFs;
	}
	inline int NumComponent(BxDFType flags) const {
		int n = 0;
		for (int i = 0; i < nBxDFs; i++) {
			if(bxdfs[i]->MatchesFlags(flags))
				n++;
		}
		return n;
	}
	static void *Alloc(u_int sz) {
		return memorypool.Alloc(sz);
	}
	static void FreeAll() {
		memorypool.FreeAll();
	}
	// In world coordinate
	Spectrum f(const Vector3 &woW, const Vector3 &wiW, BxDFType flags) const;
	Spectrum rho(BxDFType flags = BSDF_ALL) const;
	Spectrum rho(const Vector3 &woW, BxDFType flags = BSDF_ALL) const;
	// u for chose a component, sample for bxdf sampling
	Spectrum Sample_f(const Vector3 &woW, Vector3 *wiW, const float u, const Point2 &sample, float *pdf, BxDFType flags = BSDF_ALL, BxDFType *sampledType = NULL) const;
	Spectrum Sample_f(const Vector3 &woW, Vector3 *wiW, BxDFType flags = BSDF_ALL, BxDFType *sampledType = NULL) const;
	float Pdf(const Vector3 &woW, const Vector3 &wiW, BxDFType flags = BSDF_ALL) const;
private:
	Normal ng;
	Frame shadingFrame;
	float eta;
	~BSDF();
	int nBxDFs;

	BxDF *bxdfs[MAX_BXDFS];
	static MemoryPool memorypool;
};

#define BSDF_ALLOC(T) new (BSDF::Alloc(sizeof(T))) T

#endif	// LM_CORE_BSDF_H